#ifndef MMATH_H_
#define MMATH_H_

#include <d3d9.h>
#include <d3dx9.h>

#define EPSILON 0.00001f
#define EPSILONSQ 0.000000001f
#define PI 3.14159f
#define RADTODEG(integer) (integer * 180.0f / PI)
#define DEGTORAD(integer) (integer * PI / 180.0f)
#define RAND_FLOAT(min,max) (((rand()/(float)RAND_MAX)*((max)-(min)))+(min))

struct Vec2
{
	union
	{
		float v[2];
		struct
		{
			float x, y;
		};
	};

	Vec2(float fx, float fy) { x = fx; y = fy; }
	Vec2() { x = y = 0; }

	Vec2 operator+(Vec2 &v);
	Vec2 operator-(Vec2 &v);
	Vec2 operator*(float f);
	Vec2 operator/(float f);
	Vec2 operator+=(Vec2 &v);
	Vec2 operator-=(Vec2 &v);
	Vec2 operator*=(float f);
	Vec2 operator/=(float f);
	Vec2 operator-();
	bool operator!=(Vec2 &v);
	bool operator==(Vec2 &v);

	friend Vec2 operator*(float f, Vec2 &v) { return v * f; }

	void Zero() { x = y = 0; }
	void Negate();
	float LengthSq();
	float Length();
	void Normalize();
};

float AngleBetweenR(Vec2 &v1, Vec2 &v2);
float AngleBetweenD(Vec2 &v1, Vec2 &v2);
float Dot(Vec2 &v1, Vec2 &v2);
float Component(Vec2 &v1, Vec2 &v2);
Vec2 Project(Vec2 &v1, Vec2 &v2);
Vec2 Reflect(Vec2 &v, Vec2 &n);

struct Vec3
{
	union
	{
		float v[3];
		struct
		{
			float x, y, z;
		};
	};

	Vec3(float fx, float fy, float fz) { x = fx; y = fy; z = fz; }
	Vec3(Vec2 v2, float fz = 0) { x = v2.x; y = v2.y; z = fz; }
	Vec3() { x = y = z = 0; }

	Vec3 operator+(Vec3 &v);
	Vec3 operator-(Vec3 &v);
	Vec3 operator*(float f);
	Vec3 operator/(float f);
	Vec3 operator+=(Vec3 &v);
	Vec3 operator-=(Vec3 &v);
	Vec3 operator*=(float f);
	Vec3 operator/=(float f);
	Vec3 operator-();
	bool operator!=(Vec3 &v);
	bool operator==(Vec3 &v);

	friend Vec3 operator*(float f, Vec3 &v) { return v * f; }

	void Zero() { x = y = z = 0; }
	void Negate();
	float LengthSq();
	float Length();
	void Normalize();
};

float AngleBetweenR(Vec3 &v1, Vec3 &v2);
float AngleBetweenD(Vec3 &v1, Vec3 &v2);
float Dot(Vec3 &v1, Vec3 &v2);
float Component(Vec3 &v1, Vec3 &v2);
Vec3 Cross(Vec3 &v1, Vec3 &v2);
Vec3 Project(Vec3 &v1, Vec3 &v2);
Vec3 Reflect(Vec3 &v, Vec3 &n);

struct Mat3
{
	union
	{
		float mm[9];
		float m[3][3];
		struct
		{
			float xx, xy, xz;
			float yx, yy, yz;
			float zx, zy, zz;
		};
		struct
		{
			Vec2 xaxis; float xwpad;
			Vec2 yaxis; float ywpad;
			Vec2 zaxis; float zwpad;
		};
	};

	Mat3() { }
	Mat3(float fxx, float fxy, float fxz,
		float fyx, float fyy, float fyz,
		float fzx, float fzy, float fzz)
	{
		xx = fxx; xy = fxy; xz = fxz;
		yx = fyx; yy = fyy; yz = fyz;
		zx = fzx; zy = fzy; zz = fzz;
	}

	Mat3 operator*(Mat3 &m);
	Vec2 operator*(Vec2 &v);
	Mat3 operator*=(Mat3 &m);
	Mat3 operator-();

	friend Vec2 operator*(Vec2 &v, Mat3 &m);

	void MakeZero();
	void MakeIdentity();
	void Negate();
	void Transpose();
	//void Invert();
	void MakeRotate(float angle);
	void MakeTranslate(Vec2 &trans);
	void MakeScale(Vec2 &scale);

	static Mat3 GetIdentity();
	static Mat3 GetRotate(float angle);
	static Mat3 GetTranslate(Vec2 &trans);
	static Mat3 GetScale(Vec2 &scale);

private:
	void Swap(float &f1, float &f2)
	{
		float temp;
		temp = f1;
		f1 = f2;
		f2 = temp;
	}
};

struct Mat4
{
	union
	{
		float mm[16];
		float m[4][4];
		struct
		{
			float xx, xy, xz, xw;
			float yx, yy, yz, yw;
			float zx, zy, zz, zw;
			float wx, wy, wz, ww;
		};
		struct
		{
			Vec3 xaxis; float xwpad;
			Vec3 yaxis; float ywpad;
			Vec3 zaxis; float zwpad;
			Vec3 waxis; float wwpad;
		};
	};

	Mat4() { }
	Mat4(float fxx, float fxy, float fxz, float fxw,
		float fyx, float fyy, float fyz, float fyw,
		float fzx, float fzy, float fzz, float fzw,
		float fwx, float fwy, float fwz, float fww)
	{
		xx = fxx; xy = fxy; xz = fxz; xw = fxw;
		yx = fyx; yy = fyy; yz = fyz; yw = fyw;
		zx = fzx; zy = fzy; zz = fzz; zw = fzw;
		wx = fwx; wy = fwy; wz = fwz; ww = fww;
	}

	Mat4(const D3DXMATRIX &mat);

	Mat4 operator*(Mat4 &m);
	Vec3 operator*(Vec3 &v);
	Mat4 operator*=(Mat4 &m);
	Mat4 operator=(const D3DXMATRIX &mat);
	Mat4 operator-();

	friend Vec3 operator*(Vec3 &v, Mat4 &m);

	void MakeZero();
	void MakeIdentity();
	void Negate();
	void Transpose();
	void OrthoNormalInvert();
	void MakeRotateX(float angle);
	void MakeRotateY(float angle);
	void MakeRotateZ(float angle);
	void MakeRotateArbitrary(float angle, Vec3 &axis);
	void MakeTranslate(Vec3 &trans);
	void MakeScale(Vec3 &scale);
	D3DXMATRIX AsD3DXMatrix(void);

	static Mat4 GetIdentity();
	static Mat4 GetRotateX(float angle);
	static Mat4 GetRotateY(float angle);
	static Mat4 GetRotateZ(float angle);
	static Mat4 GetRotateArbitrary(float angle, Vec3 &axis);
	static Mat4 GetTranslate(Vec3 &trans);
	static Mat4 GetScale(Vec3 &scale);

private:
	void Swap(float &f1, float &f2)
	{
		float temp;
		temp = f1;
		f1 = f2;
		f2 = temp;
	}
};


struct Quaternion
{
	float w;
	union
	{
		struct
		{
			Vec3 v;
		};
		struct
		{
			float x, y, z;
		};
	};

	Quaternion() { }
	Quaternion(float fW, float fX, float fY, float fZ) { w = fW; x = fX; y = fY; z = fZ; }
	Quaternion(float fAngle, Vec3 &Axis);
	Quaternion(const Mat4 &Mat);

	void ToMatrix(Mat4 &Mat);
	void ToAngleAxis(float &fAngle, Vec3 &Axis);

	void MakeZero() { w = x = y = z = 0; }
	void MakeIdentity() { w = 1; x = y = z = 0; }
	void Conjugate() { x = -x; y = -y; z = -z; }
	float Length();
	float Norm();
	void Normalize();
	void Invert();

	Quaternion operator+(Quaternion &q);
	Quaternion operator-(Quaternion &q);
	Quaternion operator*(float f);
	Quaternion operator/(float f);
	Quaternion operator*(Quaternion &q);
	Quaternion operator+=(Quaternion &q);
	Quaternion operator-=(Quaternion &q);
	Quaternion operator*=(float f);
	Quaternion operator/=(float f);
	Quaternion operator*=(Quaternion &q);
	bool operator==(Quaternion &q);
	bool operator!=(Quaternion &q);
	Quaternion operator-();

	friend Quaternion operator*(float f, Quaternion &q);
};

float Dot(Quaternion &q1, Quaternion &q2);
Quaternion Slerp(Quaternion &q1, Quaternion &q2, float fDelta);

#endif